part of '../flutter_ble_plugin.dart';

typedef ScanResultCallback = void Function(String deviceId, String deviceName);
typedef ConnectionCallback = void Function(bool isConnected);

class FlutterBlePlugin {
  static bool _initialized = false;

  static const MethodChannel _methodChannel =
      MethodChannel('flutter_ble_plugin/methods');

  static const EventChannel _eventChannel =
      EventChannel('flutter_ble_plugin/events');

  // 事件订阅
  StreamSubscription? _eventSubscription;

  BleCallBack? _bleCallBack;

  static Future<dynamic> _initFlutterBluePlus() async {
    if (_initialized) {
      return;
    }

    _initialized = true;

    // set platform method handler
    _methodChannel.setMethodCallHandler(_methodCallHandler);
  }

  /// invoke a platform method
  static Future<dynamic> _invokeMethod(
    String method, [
    dynamic arguments,
  ]) async {
    // return value
    dynamic out;

    // only allow 1 invocation at a time (guarantees that hot restart finishes)
    _Mutex mtx = _MutexFactory.getMutexForKey("invokeMethod");
    await mtx.take();

    try {
      // initialize
      if (method != "setOptions") {
        _initFlutterBluePlus();
      }

      // log args
      print("[FBP] <$method> args: ${arguments.toString()}");

      // invoke
      out = await _methodChannel.invokeMethod(method, arguments);

      // log result
      print("[FBP] <$method> result: ${out.toString()}");
    } finally {
      mtx.give();
    }

    return out;
  }

  void setCallback(BleCallBack? callBack) {
    _bleCallBack = callBack;
    _eventSubscription?.cancel();
    if (callBack == null) {
      return;
    }

    _eventSubscription = _eventChannel.receiveBroadcastStream().listen((event) {
      event = event as Map;

      switch (event["type"]) {
        case "onScanResult":
          _bleCallBack?.onScanResult(event["mac"], event["name"]);
          break;
        case "onConnectionStateChange":
          _bleCallBack?.onConnectionStateChange(
              event["mac"], event["isConnected"]);
          break;
        case "onReceiveData":
          _bleCallBack?.onReceiveData(event["mac"], event["data"]);
          break;
        default:
          break;
      }
    });
  }

  Future<String?> getPlatformVersion() async {
    return await _invokeMethod("getPlatformVersion");
  }

  Future<void> startScan(ScanResultCallback callback) async {
    bool scanning = await isScanning();
    if (scanning) {
      await stopScan();
    }

    _invokeMethod("startScan");
  }

  Future<void> stopScan() async {
    _invokeMethod("stopScan");
  }

  Future<bool> isScanning() async {
    return await _invokeMethod("isScanning");
  }

  Future<bool> connect(String deviceId) async {
    bool scanning = await isScanning();
    if (scanning) {
      await stopScan();
    }

    return await _invokeMethod("connect", deviceId);
  }

  Future<void> disconnect(String deviceId) async {
    Map<String, dynamic> args = {
      "mac": deviceId,
    };
    _invokeMethod("disconnect", args);
  }

  Future<bool> sendData(String deviceId, Uint8List data) async {
    Map<String, dynamic> args = {
      "mac": deviceId,
      "data": data,
    };
    return await _invokeMethod("sendData", args);
  }

  static Future<dynamic> _methodCallHandler(MethodCall call) async {}
}
